#!/usr/bin/env python
# -*- mode: python; coding: utf-8; -*-
##--------------------------------------------------------------------------##
##
## Copyright (C) 1998-2003 Markus Oberhumer <markus@oberhumer.com>
## Copyright (C) 2003 Mt. Hood Playing Card Co.
## Copyright (C) 2005-2010 Skomoroh <skomoroh@users.sourceforge.net>
## Copyright (C) 2010-2013 Jeremy Austin-Bardo <tjaustinbardo@gmail.com>
##
## This program is free software: you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation, either version 3 of the License.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program.  If not, see <http://www.gnu.org/licenses/>.
##
##-- G08 -- S05 ------------------------------------------------------------##
__all__ = []

from .. import formlib as layout
from .. import pilelib as stack
from .. import quickplayscore as quickplay
from ..gamelib import info as gi, state, game
from ..hint import CautiousDefaultHint
from ..packlib import info as cs, place
from ..piles import napoleon as nstack

StackWrapper = stack.StackWrapper
GT, SL = gi.GT, gi.SL


class UD_SS_RowStack(stack.UD_SS_RowStack):
    """
    Build either in rank by same suit. _BOTTOM_IMAGE_ is shade.
    """
    _BOTTOM_IMAGE_ = 'shade'


class TheLittleCorporal_RowStack(stack.GapFromReserveBuildMethod,
                                 stack.UD_SS_RowStack):
    """
    Build either in rank by same suit. Base from _reserve_.
    """
    pass


class FreeCell1(stack.ReserveStack):
    """
    Build any 1 card. Play after both _row_ pile [-2:] gap.
    """

    def cardsPlay(self, cards):
        """
        cardsPlay(cards:tup) -> bool

        Check parent *capability* if cards plays. Cards play when neighbor
        piles are gap. (last 2 _row_)

        :param sequence cards: cards from sending pile.
        :return: pile moves cards?
        :rtype: boolean

        seealso:
          Method pysollib.stackcaps.plays for the *capability* check.
        """
        if not super(FreeCell1, self).cardsPlay(cards):
            return False
        return all(s.cards for s in self.game.s['rows'][-2:])


class FreeCell2(stack.ReserveStack):
    """
    Build any 1 card. Play after neighbor _row_ pile gap pile (id - 2).
    """

    def cardsPlay(self, cards):
        """
        cardsPlay(cards:tup) -> bool

        Check parent *capability* if cards plays. Cards play when neighbor
        pile is gap. (stack.id - 2)

        :param sequence cards: cards from sending pile.
        :return: pile moves cards?
        :rtype: boolean

        seealso:
          Method pysollib.stackcaps.plays for the *capability* check.
        """
        if not super(FreeCell2, self).cardsPlay(cards):
            return False
        return self.game.s['rows'][self.id - 2].cards


class BusyCards_FreeCell(stack.ReserveStack):
    """
    Build any 1 card. Play after 2 neighbor _row_ pile gap pile [*2:*2+2].
    """

    def cardsPlay(self, cards):
        """
        cardsPlay(cards:seq) -> bool

        Checks parent *capability* and whether 2 neighbor _row_ is gap.
        Cards must be from the waste.

        :param tuple cards: cards to accept from another pile
        :return: cards moveable?
        :rtype: boolean
        """
        if not super(BusyCards_FreeCell, self).cardsPlay(cards):
            return False
        index = self.game.s['reserves'].index(self) * 2
        return not all(s.cards for s in self.game.s['rows'][index:index + 2])


class DerKleineNapoleon(game.Game):
    """
    Finish cards on _foundation_ within a turn.

    _row_
      Pile 8 over 2 col E W _foundation. Init deal 5 open card. Build either
      in rank by same suit wrap. Play top (edge).

    _reserve_
      Pile 2 over 2 col E W _freecell_. Init deal 4 open card. Build none.
      Play top (edge).

    _freecell_
      Pile 1. Build any 1 card. Play when _reserve_ gap.

    _foundation_
      Pile 4 vert. Init 4 same rank base card. Build choice in rank by
      same suit from base wrap. Play top.
    ------
    :cvar Stack _talon_: stack.InitialDealTalon
    :cvar Stack _foundation_: braid.Foundation
    :cvar Stack _row_: UD_SS_RowStack (mod=13)
    :cvar Stack _reserves_: stack.BasicRowStack
    :cvar Stack _freecell_: FreeCell1
    :cvar Hint _hint_: hint.CautiousDefaultHint
    """
    __info__ = dict(id=167, name="Der kleine Napoleon", game_type=(GT.NAPOLEON
                    | GT.OPEN), decks=1, redeals=0, version='4.20',
                    skill_level=SL.MOSTLY_SKILL)
    __author__ = 'Markus Oberhumer'
    __version__ = 7.70

    _talon_ = stack.InitialDealTalon
    _foundation_ = nstack.Foundation
    _row_ = StackWrapper(UD_SS_RowStack, mod=13)
    _reserve_ = stack.BasicRowStack
    _freecell_ = FreeCell1
    _hint_ = CautiousDefaultHint

    def _create_game(self, rows=8, playcards=4, **kw):
        l, s = layout.Layout(self), self.s
        xc = l.XM + l.XS
        dx = playcards * xc
        self.table_size = (2 * dx + xc, 3 * l.YM + (len(rows) / 2 + 2) * l.YS)
        offset = lambda n: ([l.XS] * (playcards if n else -playcards) +
                            [l.XOFFSET] * (13 if n else -13), 0)
        y = l.YM
        for i in range(rows / 2):  # Vert
            x = dx
            for j in range(2):  # Horz
                s['rows'].append(self._row_(x, y, game=self, offset=offset(j)))
                x += 2 * xc
            y += l.YS
        x, y = dx, self.height - 2 * l.YS
        ddx = (dx, dx + 2 * xc, dx + xc,)
        if issubclass(self._freecell_, FreeCell2):
            ddx = (dx - xc, dx + 3 * xc, dx, dx + 2 * xc,)
        for j, x in zip(range(2), ddx):  # Horz
            s['rows'].append(self._reserve_(x, y, game=self, offset=offset(j)))
        for x in ddx[2:]:  # Horz
            s['reserves'].append(self._freecell_(x, y, game=self))
        x, y = dx + xc, l.YM
        for i in l.SUITS:
            s['foundations'].append(self._foundation_(x, y, game=self, suit=i))
            y += l.YS
        y = self.height - l.YS
        s['talon'] = self._talon_(x, y, game=self)
        l.defaultStackGroups()
        return l

    def _pack_place(self, cards):
        """
        _pack_place(cards:seq(Card ...)) -> seq(Card ...)

        Overide to return any 1 rank on top by suit, deck for _foundation_ deal.
        """
        return place.MoveToBottom(cards, place.AnyOneRank(cards[-1]))

    def _start(self, rows=5, **kw):
        for i in range(rows - 1):
            self.s['talon'].dealRow(rows=self.s['rows'][:8], frames=0)
        self._play_sound('deal')
        self.s['talon'].dealRow(rows=self.s['rows'][:8])
        for i in range(4):
            self.s['talon'].dealRow(rows=self.s['rows'][8:])
        self.s['talon'].dealBaseCards(ncards=4)

    @state.not_state('is_preview')
    def _update_text(self):
        if not self.texts['info']:
            return
        t = ""
        f = self.s['foundations'][0]
        if f.cards:
            t = self.RANKS[f.cap['base_rank']]
            t += {1: _(" Ascending"), -1: _(" Descending")}.get(f.cap['dir'], '')
        self.texts['info'].config(text=t)


class DerFreieNapoleon(DerKleineNapoleon):
    """
    Finish cards on _foundation_ within a turn.

    _row_
      Pile 8 horz S _foundation. Init deal 5 open card. Build either
      in rank by same suit wrap. Play top.

    _reserve_
      Pile 2 over 2 col E _row_. Init deal 4 open card. Build none. Play top.

    _freecell_
      Pile 1 N _reserve_. Build any 1 card. Play when _reserve_ gap.

    _foundation_
      Pile 4 horz N. Init 4 same rank base card. Build choice in rank by
      same suit from base wrap. Play top.
    ------
    :cvar Stack _talon_: stack.InitialDealTalon
    :cvar Stack _foundation_: braid.Foundation
    :cvar Stack _row_: UD_SS_RowStack (mod=13)
    :cvar Stack _reserves_: stack.BasicRowStack
    :cvar Stack _freecell_: FreeCell1
    :cvar Hint _hint_: hint.CautiousDefaultHint
    """
    __info__ = dict(id=168, name="Der freie Napoleon", game_type=(GT.NAPOLEON
                    | GT.OPEN), decks=1, redeals=0, version='4.20',
                    skill_level=SL.MOSTLY_SKILL)
    __author__ = 'Markus Oberhumer'
    __version__ = 7.70

    def _create_game(self, rows=8, playcards=14, **kw):
        l, s = layout.Layout(self), self.s
        self.table_size = (2 * l.XM + (rows + 2) * l.XS,
                     l.YM + 2 * l.YS + max(playcards * l.YOFFSET, 2 * l.YS))
        x, y = l.XS, l.YM + l.YS
        for j in range(rows):
            s['rows'].append(self._row_(x, y, game=self))
            x += l.XS
        x += l.XM
        dx = x
        for j in range(2):
            s['rows'].append(self._reserve_(x, y, self))
            x += l.XS
        self._set_region(s['rows'], l._region(y=y - l.HCH))
        y = l.YM
        ddx = (x + l.XS / 2,)
        if issubclass(self._freecell_, FreeCell2):
            ddx = (dx, dx + l.XS,)
        for x in ddx:
            s['reserves'].append(self._freeCell_(x, y, game=self))
        x = l.XM + (rows - l.NSUITS) * l.XS / 2
        for i in l.SUITS:
            s['foundations'].append(self._foundation_(x, y, game=self, suit=i))
            x += l.XS
        self.game.texts['info'] = l.create_info_text(s['foundations'][-1], "se")
        x, y = l.XM, self.height - l.YS
        s['talon'] = self._talon_(x, y, game=self)
        l.defaultStackGroups()
        return l


class Napoleon(DerKleineNapoleon):
    """
    Finish cards on _foundation_ within a turn.

    _row_
      Pile 8 over 2 col E W _foundation. Init deal 5 open card. Build either
      in rank by same suit wrap. Play top (edge).

    _reserve_
      Pile 2 over 2 col E W _freecell_. Init deal 4 open card. Build none.
      Play top (edge).

    _freecell_
      Pile 2 horz. Build any 1 card. Play when _reserve_ gap.

    _foundation_
      Pile 4 vert. Init 4 same rank base card. Build choice in rank by
      same suit from base wrap. Play top.
    ------
    :cvar Stack _talon_: stack.InitialDealTalon
    :cvar Stack _foundation_: braid.Foundation
    :cvar Stack _row_: UD_SS_RowStack (mod=13)
    :cvar Stack _reserves_: stack.BasicRowStack
    :cvar Stack _freecell_: FreeCell1
    :cvar Hint _hint_: hint.CautiousDefaultHint
    """
    __info__ = dict(id=169, name="Napoleon", game_type=(GT.NAPOLEON |
                    GT.OPEN), decks=1, redeals=0, skill_level=SL.MOSTLY_SKILL,
                    version='4.20')
    __author__ = 'Markus Oberhumer'
    __version__ = 7.70

    _freecell_ = FreeCell2


class FreeNapoleon(DerFreieNapoleon):
    """
    Finish cards on _foundation_ within a turn.

    _row_
      Pile 8 horz S _foundation. Init deal 5 open card. Build either
      in rank by same suit wrap. Play top.

    _reserve_
      Pile 2 over 2 col E _row_. Init deal 4 open card. Build none. Play top.

    _freecell_
      Pile 2 horz N _reserve_. Build any 1 card. Play when _reserve_ gap.

    _foundation_
      Pile 4 horz N. Init 4 same rank base card. Build choice in rank by
      same suit from base wrap. Play top.
    ------
    :cvar Stack _talon_: stack.InitialDealTalon
    :cvar Stack _foundation_: braid.Foundation
    :cvar Stack _row_: UD_SS_RowStack (mod=13)
    :cvar Stack _reserves_: stack.BasicRowStack
    :cvar Stack _freecell_: FreeCell1
    :cvar Hint _hint_: hint.CautiousDefaultHint
    """
    __info__ = dict(id=170, name="Free Napoleon", game_type=(GT.NAPOLEON |
                    GT.OPEN), decks=1, redeals=0, skill_level=SL.MOSTLY_SKILL,
                    version='4.20')
    __author__ = 'Markus Oberhumer'
    __version__ = 7.70

    _freecell_ = FreeCell2


class Master(FreeNapoleon):
    """
    Finish cards on _foundation_ within a turn.

    _row_
      Pile 8 horz S _foundation. Init deal 5 open card. Build either
      in rank by same suit wrap. Play top.

    _reserve_
      Pile 2 over 2 col E _row_. Init deal 4 open card. Build none. Play top.

    _freecell_
      Pile 2 horz N _reserve_. Build any 1 card. Play when _reserve_ gap.

    _foundation_
      Pile 4 horz N. Init deal base ACE. Build up in rank by same suit. Play
      top.
    ------
    :cvar Stack _talon_: stack.InitialDealTalon
    :cvar Stack _foundation_: SS_Foundation
    :cvar Stack _row_: UD_SS_RowStack (mod=13)
    :cvar Stack _reserves_: stack.BasicRowStack
    :cvar Stack _freecell_: FreeCell1
    :cvar Hint _hint_: hint.CautiousDefaultHint
    """
    __info__ = dict(id=536, name="Master", game_type=(GT.NAPOLEON | GT.OPEN),
                    decks=1, redeals=0, skill_level=SL.MOSTLY_SKILL,
                    version='0.93fc')
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _foundation_ = stack.SS_Foundation

    def _create_game(self, **kw):
        l = super(Master, self)._create_game(**kw)
        self.texts['info'] = None
        return l

    def _pack_place(self, cards):
        """
        _pack_place(cards:seq(Card ...)) -> seq(Card ...)

        Overide to return ACE on bottom by suit, deck for _foundation_ deal.
        """
        return place.MoveToBottom(cards, place.AllAce)


class TheLittleCorporal(DerFreieNapoleon):
    """
    Finish cards on _foundation_ within a turn.

    _row_
      Pile 10 horz S _foundation. Init deal [4, 5, 5, 5, 5, 5, 5, 5, 5, 4]
      open card. Build either in rank by same suit wrap. Play top.

    _reserve_
      Pile 1 NE _row_. Init deal 4 open card. Build any. Play top.
      Play _row_ on gap.

    _foundation_
      Pile 4 horz N. Init 4 same rank base card. Build choice in rank by
      same suit from base wrap. Play top.
    ------
    :cvar Stack _talon_: stack.InitialDealTalon
    :cvar Stack _foundation_: braid.Foundation
    :cvar Stack _row_: TheLittleCorporal_RowStack (mod=13)
    :cvar Stack _reserves_: stack.ReserveStack, max_cards=MAX
    :cvar Stack _freecell_: None
    :cvar Hint _hint_: hint.CautiousDefaultHint
    """
    __info__ = dict(id=537, name="The Little Corporal", game_type=(GT.NAPOLEON
                    | GT.OPEN), decks=1, version='0.93fc',
                    skill_level=SL.MOSTLY_SKILL)
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _row_ = StackWrapper(TheLittleCorporal_RowStack, mod=13)
    _reserve_ = StackWrapper(stack.ReserveStack, max_cards=cs.MAX)
    _freecell_ = None

    def _create_game(self, rows=10, playcards=14, **kw):
        l, s = layout.Layout(self), self.s
        self.table_size = (2 * l.XM + (rows + 1) * l.XS,
                     l.YM + 2 * l.YS + max(playcards * l.YOFFSET, 2 * l.YS))
        x, y = l.XS, l.YM + l.YS
        for j in range(rows):
            s['rows'].append(self._row_(x, y, game=self))
            x += l.XS
        x += l.XM
        y = l.YM
        s['reserves'].append(self._reserve_(x, y, game=self))
        l.set_ncards_text(s['reserve'][0], 'se')
        x = l.XM + (rows - l.NSUITS) * l.XS / 2
        for i in l.SUITS:
            s['foundations'].append(self._foundation_(x, y, game=self, suit=i))
            x += l.XS
        self.game.texts['info'] = l.create_info_text(s['foundations'][-1], "se")
        x, y = l.XM, self.height - l.YS
        s['talon'] = self._talon_(x, y, game=self)
        l.defaultStackGroups()
        return l

    def _start(self, rows=5, **kw):
        for i in range(rows - 1):
            self.s['talon'].dealRow(rows=self.s['rows'], frames=0)
        self._play_sound('deal')
        self.s['talon'].dealRow(rows=self.s['rows'][1:-1])
        self.s['talon'].dealBaseCards(ncards=4)

    _quickplay_ = quickplay.Needle


class Bonaparte(TheLittleCorporal):
    """
    Finish cards on _foundation_ within a turn.

    _row_
      Pile 8 horz S _foundation. Init deal 6 open card. Build either in rank
      by same suit wrap. Play top.

    _reserve_
      Pile 1 NE _row_. Init deal 4 open card. Build any. Play top.
      Play _row_ on gap.

    _foundation_
      Pile 4 horz N. Init 4 same rank base card. Build choice in rank by
      same suit from base wrap. Play top.
    ------
    :cvar Stack _talon_: stack.InitialDealTalon
    :cvar Stack _foundation_: braid.Foundation
    :cvar Stack _row_: TheLittleCorporal_RowStack (mod=13)
    :cvar Stack _reserves_: stack.ReserveStack, max_cards=MAX
    :cvar Stack _freecell_: None
    :cvar Hint _hint_: hint.CautiousDefaultHint
    """
    __info__ = dict(id=538, name="Bonaparte", game_type=(GT.NAPOLEON |
                    GT.OPEN | GT.ORIGINAL), decks=1, redeals=0,
                    skill_level=SL.MOSTLY_SKILL, version='0.93fc')
    __author__ = 'Skomoroh'
    __version__ = 7.70

    def _create_game(self, **kw):
        kw.setdefault('rows', 8)
        return super(Bonaparte, self)._create_game(**kw)

    def _start(self, rows=6, **kw):
        for i in range(rows - 1):
            self.s['talon'].dealRow(frames=0)
        self._play_sound('deal')
        self.s['talon'].dealRow()
        self.s['talon'].dealBaseCards(ncards=4)


class BusyCards(game.Game):
    """
    Finish cards on _foundation_ within a turn.

    _row_
      Pile 12 horz S _row_. Init deal 8 open card. Build either in rank
      by same suit. Play top.

    _reserve_
      Pile 6 horz S _foundation. Build any. Play top _row_ neighbor are gaps.

    _foundation_
      Pile 8 horz N. Init base ACE, base KING. Build in rank by
      same suit: W up. E down. Play top.
    ------
    :cvar Stack _talon_: stack.InitialDealTalon
    :cvar Stack _foundations_: stack.SS_Foundation,
      stack.SS_Foundation (dir=-1, base_rank=KING)
    :cvar Stack _row_: stack.UD_SS_RowStack
    :cvar Stack _reserves_: BusyCards_FreeCell
    :cvar Hint _hint_: hint.CautiousDefaultHint
    """
    __info__ = dict(id=705, name="Busy Cards", game_type=(GT.NAPOLEON |
                    GT.OPEN | GT.ORIGINAL), decks=2, redeals=0,
                    skill_level=SL.MOSTLY_SKILL, version='1.00fc')
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _talon_ = stack.InitialDealTalon
    _foundations_ = (stack.SS_Foundation,
                     StackWrapper(stack.SS_Foundation, dir=-1,
                                  base_rank=cs.KING),)
    _row_ = stack.UD_SS_RowStack
    _reserve_ = BusyCards_FreeCell
    _hint_ = CautiousDefaultHint

    def _create_game(self, rows=12, playcards=16, **kw):
        l, s = layout.Layout(self), self.s
        self.table_size = (l.XM + rows * l.XS,
                     l.YM + 3 * l.YS + playcards * l.YOFFSET)
        center = lambda p: (self.width - p * l.XS) / 2
        x, y = center(l.NSUTIS), l.YM
        for _foundation_ in self._foundations_:
            for i in l.SUITS:
                s['foundations'].append(_foundation_(x, y, game=self, suit=i))
                x += l.XS
            x += l.XS
        x = center(rows) + l.XS / 2
        y += l.YS
        for i in range(rows / 2):
            s['reserves'].append(self._reserve_(x, y, game=self))
            x += 2 * l.XS
        x = center(rows)
        y += l.YS
        for i in range(rows):
            s['rows'].append(self._row_(x, y, game=self))
            x += l.XS
        x, y = l.XM, self.height - l.YS
        s['talon'] = self._talon_(x, y, self)
        l.defaultStackGroups()
        return l

    def _pack_place(self, cards):
        """
        _pack_place(cards:seq(Card ...)) -> seq(Card ...)

        Overide to return ACE and KING of suit on top by rank, suit, deck for
        _foundation_ deal.
        """
        return place.MoveToTop(cards, place.OneAceKing)

    def _start(self, rows=8, **kw):
        self.s['talon'].dealRow(rows=self.s['foundations'], frames=0)
        for i in range(rows - 1):
            self.s['talon'].dealRow(frames=0)
        self._play_sound('deal')
        self.s['talon'].dealRow()

PROVIDES = [DerKleineNapoleon, DerFreieNapoleon, Napoleon, FreeNapoleon,
            Master, TheLittleCorporal, Bonaparte, BusyCards]